#include <riscv/genarator.h>

#define INST_LIST_TYPE std::vector<Inst*>

#define GEN_INST_FUNC(FUNC_NAME, INST, RD, SRC1, SRC2) \
  void Genarator::FUNC_NAME(INST_LIST_TYPE & inst_list, Reg RD, Reg SRC1, Reg SRC2) { \
    INST * inst = new INST(); \
    inst->RD = RD; \
    inst->SRC1 = SRC1; \
    inst->SRC2 = SRC2; \
    inst_list.push_back(inst); \
  }

#define GEN_INST_FUNC_OFF(FUNC_NAME, INST, RD, OFF, SRC) \
  void Genarator::FUNC_NAME(INST_LIST_TYPE & inst_list, Reg RD, int OFF, Reg SRC) { \
    INST * inst = new INST(); \
    inst->RD = RD; \
    inst->OFF = OFF; \
    inst->SRC = SRC; \
    inst_list.push_back(inst); \
  }

namespace riscv {
  Genarator::Genarator(CompUnit * comp_unit) {
    this->comp_unit = comp_unit;

    // initial temp reg map
    std::array<Reg, 7>temp_regs = {
      Reg::t0, Reg::t1, Reg::t2, Reg::t3, Reg::t4, Reg::t5, Reg::t6
    };
    for(Reg & reg : temp_regs) {
      this->temp_reg_map[reg] = false;
    }

    // initial temp freg map
    std::array<FReg, 12>temp_fregs = {
      FReg::ft0, FReg::ft1, FReg::ft2, FReg::ft3, FReg::ft4, FReg::ft5, FReg::ft6,
      FReg::ft7, FReg::ft8, FReg::ft9, FReg::ft10, FReg::ft11,
    };
    for(FReg & freg : temp_fregs) {
      this->temp_freg_map[freg] = false;
    }

    this->float_number_i = 0;

    this->block_i = 0;
    this->block_begin_i = 0;
    this->block_end_i = 0;
  }

  Reg Genarator::alloc_temp_reg() {
    for(auto & pair : this->temp_reg_map) {
      if(GET_VAL(pair) == false) {
        GET_VAL(pair) = true;
        return GET_KEY(pair);
      }
    }
    Panic("temp reg is over");
  }

  FReg Genarator::alloc_temp_freg() {
    for(auto & pair : this->temp_freg_map) {
      if(GET_VAL(pair) == false) {
        GET_VAL(pair) = true;
        return GET_KEY(pair);
      }
    }
    Panic("temp freg is over");
  }

  std::set<Reg> Genarator::get_alloced_reg() {
    std::set<Reg> res;
    for(auto & pair : this->temp_reg_map) {
      if(GET_VAL(pair) == true) {
        res.insert(GET_KEY(pair));
      }
    }
    return res;
  }

  void Genarator::free_temp_reg(Reg temp_reg) {
    Assert(this->temp_reg_map.find(temp_reg) != this->temp_reg_map.end(),
        "temp reg map not include this reg:%s", Regs::get_reg_name(temp_reg).c_str());
    Assert(this->temp_reg_map[temp_reg] != false,
        "repeat free temp reg:%s", Regs::get_reg_name(temp_reg).c_str());
    this->temp_reg_map[temp_reg] = false;
  }

  void Genarator::free_temp_reg(FReg temp_freg) {
    Assert(this->temp_freg_map.find(temp_freg) != this->temp_freg_map.end(),
        "temp reg map not include this reg:%s", Regs::get_reg_name(temp_freg).c_str());
    Assert(this->temp_freg_map[temp_freg] != false,
        "repeat free temp reg:%s", Regs::get_reg_name(temp_freg).c_str());
    this->temp_freg_map[temp_freg] = false;
  }

  Global * Genarator::create_global(std::string name, int size, int init_number) {
    Global * global = new Global();
    global->name = name;
    global->size = size;
    global->init_number = init_number;
    this->comp_unit->global_list.push_back(global);
    return global;
  }

  Func * Genarator::create_func(const std::string & name) {
    Func * func = new Func();
    func->name = name;
    this->comp_unit->func_list.push_back(func);
    return func;
  }

  FloatNumber * Genarator::create_float_number(float number) {
    FloatNumber * float_number = new FloatNumber(++this->float_number_i, number);
    this->comp_unit->float_list.push_back(float_number);
    return float_number;
  }

  Block * Genarator::create_block(Func & func) {
    Block * block = new Block(++this->block_i);
    func.block_list.push_back(block);
    return block;
  }

  Block * Genarator::create_block_begin(Func & func) {
    Assert(func.block_begin == nullptr, "block_begin just init once");
    Block * block = new Block(std::string(".BEGIN")
        + std::to_string(++this->block_begin_i));
    func.block_begin = block;
    return block;
  }

  Block * Genarator::create_block_end(Func & func) {
    Assert(func.block_end == nullptr, "block_end just init once");
    Block * block = new Block(std::string(".END")
        + std::to_string(++this->block_end_i));
    func.block_end = block;
    return block;
  }

  void Genarator::gen_inst_addi(std::vector<Inst*> & inst_list, Reg rd, Reg src, int imm) {
    InstAddi * inst_addi = new InstAddi();
    inst_addi->rd = rd;
    inst_addi->src = src;
    inst_addi->imm = imm;
    inst_list.push_back(inst_addi);
  }

  GEN_INST_FUNC(gen_inst_add, InstAdd, rd, src1, src2);
  GEN_INST_FUNC(gen_inst_sub, InstSub, rd, src1, src2);
  GEN_INST_FUNC(gen_inst_mul, InstMul, rd, src1, src2);
  GEN_INST_FUNC(gen_inst_div, InstDiv, rd, src1, src2);

  void Genarator::gen_inst_sd(std::vector<Inst*> & inst_list, Reg rd, int off, Reg src) {
    InstSd * inst_sd = new InstSd();
    inst_sd->rd = rd;
    inst_sd->off = off;
    inst_sd->src = src;
    inst_list.push_back(inst_sd);
  }

  void Genarator::gen_inst_sw(std::vector<Inst*> & inst_list, Reg rd, int off, Reg src) {
    InstSw * inst_sw = new InstSw();
    inst_sw->rd = rd;
    inst_sw->off = off;
    inst_sw->src = src;
    inst_list.push_back(inst_sw);
  }

  void Genarator::gen_inst_ld(std::vector<Inst*> & inst_list, Reg rd, int off, Reg src) {
    InstLd * inst_ld = new InstLd();
    inst_ld->rd = rd;
    inst_ld->off = off;
    inst_ld->src = src;
    inst_list.push_back(inst_ld);
  }

  GEN_INST_FUNC_OFF(gen_inst_lw, InstLw, rd, off, src);

  void Genarator::gen_inst_j(std::vector<Inst*> & inst_list, Block * block) {
    InstJ * inst_j = new InstJ();
    inst_j->block = block;
    inst_list.push_back(inst_j);
  }

  void Genarator::gen_inst_bne(std::vector<Inst*> & inst_list, Reg src1, Reg src2, Block * block) {
    InstBne * inst_bne = new InstBne();
    inst_bne->src1 = src1;
    inst_bne->src2 = src2;
    inst_bne->block = block;
    inst_list.push_back(inst_bne);
  }

  void Genarator::gen_inst_beq(std::vector<Inst*> & inst_list, Reg src1, Reg src2, Block * block) {
    InstBeq * inst_beq = new InstBeq();
    inst_beq->src1 = src1;
    inst_beq->src2 = src2;
    inst_beq->block = block;
    inst_list.push_back(inst_beq);
  }

  void Genarator::gen_inst_li(std::vector<Inst*> & inst_list, Reg reg, int imm) {
    InstLi * inst_li = new InstLi();
    inst_li->reg = reg;
    inst_li->imm = imm;
    inst_list.push_back(inst_li);
  }

  void Genarator::gen_inst_mv(std::vector<Inst*> & inst_list, Reg rd , Reg src) {
    InstMv * inst_mv = new InstMv();
    inst_mv->rd = rd;
    inst_mv->src = src;
    inst_list.push_back(inst_mv);
  }

  void Genarator::gen_inst_call(std::vector<Inst*> & inst_list, Func * func) {
    InstCall * inst_call = new InstCall();
    inst_call->func = func;
    inst_list.push_back(inst_call);
  }

  void Genarator::gen_inst_ret(std::vector<Inst*> & inst_list) {
    InstRet * inst_ret = new InstRet();
    inst_list.push_back(inst_ret);
  }

  void Genarator::gen_inst_lla(std::vector<Inst*> & inst_list, Reg rd, Global * global_src) {
    InstLla * inst_lla = new InstLla();
    inst_lla->rd = rd;
    inst_lla->global_float = global_src;
    inst_list.push_back(inst_lla);
  }

  void Genarator::gen_inst_lla(std::vector<Inst*> & inst_list, Reg rd, FloatNumber * float_number) {
    InstLla * inst_lla = new InstLla();
    inst_lla->rd = rd;
    inst_lla->global_float = float_number;
    inst_list.push_back(inst_lla);
  }

  void Genarator::gen_inst_sext_w(std::vector<Inst*> & inst_list, Reg rd, Reg src) {
    InstSextW * inst_sext_w = new InstSextW();
    inst_sext_w->rd = rd;
    inst_sext_w->src = src;
    inst_list.push_back(inst_sext_w);
  }

  void Genarator::gen_inst_fadd_d(std::vector<Inst*> & inst_list, FReg rd, FReg src1, FReg src2) {
    InstFaddD * inst_fadd_d = new InstFaddD();
    inst_fadd_d->rd = rd;
    inst_fadd_d->src1 = src1;
    inst_fadd_d->src2 = src2;
    inst_list.push_back(inst_fadd_d);
  }

  void Genarator::gen_inst_fsub_d(std::vector<Inst*> & inst_list, FReg rd, FReg src1, FReg src2) {
    InstFsubD * inst_fsub_d = new InstFsubD();
    inst_fsub_d->rd = rd;
    inst_fsub_d->src1 = src1;
    inst_fsub_d->src2 = src2;
    inst_list.push_back(inst_fsub_d);
  }

  void Genarator::gen_inst_fmul_d(std::vector<Inst*> & inst_list, FReg rd, FReg src1, FReg src2) {
    InstFmulD * inst_fmul_d = new InstFmulD();
    inst_fmul_d->rd = rd;
    inst_fmul_d->src1 = src1;
    inst_fmul_d->src2 = src2;
    inst_list.push_back(inst_fmul_d);
  }

  void Genarator::gen_inst_fdiv_d(std::vector<Inst*> & inst_list, FReg rd, FReg src1, FReg src2) {
    InstFdivD * inst_fdiv_d = new InstFdivD();
    inst_fdiv_d->rd = rd;
    inst_fdiv_d->src1 = src1;
    inst_fdiv_d->src2 = src2;
    inst_list.push_back(inst_fdiv_d);
  }

  void Genarator::gen_inst_fsd(std::vector<Inst*> & inst_list, FReg rd, int off, Reg src) {
    InstFsd * inst_fsd = new InstFsd();
    inst_fsd->rd = rd;
    inst_fsd->off = off;
    inst_fsd->src = src;
    inst_list.push_back(inst_fsd);
  }

  void Genarator::gen_inst_fsw(std::vector<Inst*> & inst_list, FReg rd, int off, Reg src) {
    InstFsw * inst_fsw = new InstFsw();
    inst_fsw->rd = rd;
    inst_fsw->off = off;
    inst_fsw->src = src;
    inst_list.push_back(inst_fsw);
  }

  void Genarator::gen_inst_fld(std::vector<Inst*> & inst_list, FReg rd, int off, Reg src) {
    InstFld * inst_fld = new InstFld();
    inst_fld->rd = rd;
    inst_fld->off = off;
    inst_fld->src = src;
    inst_list.push_back(inst_fld);
  }

  void Genarator::gen_inst_flw(std::vector<Inst*> & inst_list, FReg rd, int off, Reg src) {
    InstFlw * inst_flw = new InstFlw();
    inst_flw->rd = rd;
    inst_flw->off = off;
    inst_flw->src = src;
    inst_list.push_back(inst_flw);
  }

  void Genarator::gen_inst_fmv_s(std::vector<Inst*> & inst_list, FReg rd, FReg src) {
    InstFmvS * inst_fmv_s = new InstFmvS();
    inst_fmv_s->rd = rd;
    inst_fmv_s->src = src;
    inst_list.push_back(inst_fmv_s);
  }

  void Genarator::gen_inst_fmv_d(std::vector<Inst*> & inst_list, FReg rd, FReg src) {
    InstFmvD * inst_fmv_d = new InstFmvD();
    inst_fmv_d->rd = rd;
    inst_fmv_d->src = src;
    inst_list.push_back(inst_fmv_d);
  }

  void Genarator::gen_inst_fmv_d_x(std::vector<Inst*> & inst_list, FReg rd, Reg src) {
    InstFmvDX * inst_fmv_d_x = new InstFmvDX();
    inst_fmv_d_x->rd = rd;
    inst_fmv_d_x->src = src;
    inst_list.push_back(inst_fmv_d_x);
  }

  void Genarator::gen_inst_fmv_x_d(std::vector<Inst*> & inst_list, Reg rd, FReg src) {
    InstFmvXD * inst_fmv_x_d = new InstFmvXD();
    inst_fmv_x_d->rd = rd;
    inst_fmv_x_d->src = src;
    inst_list.push_back(inst_fmv_x_d);
  }

  void Genarator::gen_inst_fcvt_d_s(std::vector<Inst*> & inst_list, FReg rd, FReg src) {
    InstFcvtDS * inst_fcvt_d_s = new InstFcvtDS();
    inst_fcvt_d_s->rd = rd;
    inst_fcvt_d_s->src = src;
    inst_list.push_back(inst_fcvt_d_s);
  }

  void Genarator::gen_inst_fcvt_s_d(std::vector<Inst*> & inst_list, FReg rd, FReg src) {
    InstFcvtSD * inst_fcvt_s_d = new InstFcvtSD();
    inst_fcvt_s_d->rd = rd;
    inst_fcvt_s_d->src = src;
    inst_list.push_back(inst_fcvt_s_d);
  }

  void Genarator::gen_inst_fcvt_l_d(std::vector<Inst*> & inst_list, Reg rd, FReg src, FcvtRm rm) {
    InstFcvtLD * inst_fcvt_l_d = new InstFcvtLD();
    inst_fcvt_l_d->rd = rd;
    inst_fcvt_l_d->src = src;
    inst_fcvt_l_d->rm = rm;
    inst_list.push_back(inst_fcvt_l_d);
  }

  void Genarator::gen_inst_fcvt_d_l(std::vector<Inst*> & inst_list, FReg rd, Reg src, FcvtRm rm) {
    InstFcvtDL * inst_fcvt_d_l = new InstFcvtDL();
    inst_fcvt_d_l->rd = rd;
    inst_fcvt_d_l->src = src;
    inst_fcvt_d_l->rm = rm;
    inst_list.push_back(inst_fcvt_d_l);
  }
}
